package cn.meomis.common.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

/**
 * 个性化登录失败处理， 区分密码错和超过登录次数  
 * 
 * @author cjy
 * 
 * 参照SimpleUrlAuthenticationFailureHandler
 */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
	private static final Logger logger = Logger.getLogger(AccessDecisionManager.class);

    private String defaultFailureUrl;
    private String sessionFailureUrl;
    private boolean forwardToDestination = false;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public MyAuthenticationFailureHandler() {
    }

    public MyAuthenticationFailureHandler(String defaultFailureUrl) {
        setDefaultFailureUrl(defaultFailureUrl);
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {

    	if( exception instanceof SessionAuthenticationException ){
            if (sessionFailureUrl == null) {
                logger.debug("No failure URL set, sending 503 service unavailable");

                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Authentication Failed: " + exception.getMessage());
            } else {
                if (forwardToDestination) {
                    logger.debug("Forwarding to " + sessionFailureUrl);

                    request.getRequestDispatcher(sessionFailureUrl).forward(request, response);
                } else {
                    logger.debug("Redirecting to " + sessionFailureUrl);
                    redirectStrategy.sendRedirect(request, response, sessionFailureUrl);
                }
            }
    		return;
    	}
        if (defaultFailureUrl == null) {
            logger.debug("No failure URL set, sending 401 Unauthorized error");

            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage());
        } else {
            if (forwardToDestination) {
                logger.debug("Forwarding to " + defaultFailureUrl);

                request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
            } else {
                logger.debug("Redirecting to " + defaultFailureUrl);
                redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
            }
        }
    }

    /**
     * The URL which will be used as the failure destination.
     *
     * @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp".
     */
    public void setDefaultFailureUrl(String defaultFailureUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl),
                "'" + defaultFailureUrl + "' is not a valid redirect URL");
        this.defaultFailureUrl = defaultFailureUrl;
    }
    
    /**
     * The URL which used when session failure
     * @param sessionFailureUrl
     */
    public void setSessionFailureUrl(String sessionFailureUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(sessionFailureUrl),
                "'" + sessionFailureUrl + "' is not a valid redirect URL");
        this.sessionFailureUrl = sessionFailureUrl;
    }
    
    protected boolean isUseForward() {
        return forwardToDestination;
    }

    /**
     * If set to <tt>true</tt>, performs a forward to the failure destination URL instead of a redirect. Defaults to
     * <tt>false</tt>.
     */
    public void setUseForward(boolean forwardToDestination) {
        this.forwardToDestination = forwardToDestination;
    }

    /**
     * Allows overriding of the behaviour when redirecting to a target URL.
     */
    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }

    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }
}
